#include "FormBase64.h"
#include "tools/base64.h"

FormBase64Proc::FormBase64Proc(QWidget * parent)
    : BaseFormWidget(parent)
    , m_pQTextEdit(nullptr)
    , m_pQTextBrowser(nullptr)
    , m_eOperation(BASE64_TYPE_UNKNOW)
    , m_bCheckImg(false)
    , m_pQPBCheckImg(nullptr)
    , m_bUpdate(false)
    , m_pQPBUpdate(nullptr)
    , m_pQPBlog(nullptr)
    , m_pQPBDellog(nullptr)
{
    m_strLastData.clear();

    m_pQTextEdit = new QTextEdit(this);
    addWidget2BaseLayout(m_pQTextEdit);
    m_pQTextEdit->setPlaceholderText(QStringLiteral("输入区域可粘贴待编解码文本，或将文件拖拽到输入区域中自动识别"));

    QFont font;
    font.setPixelSize(GLOBAL_SHOWFONTSIZE);
    m_pQTextEdit->setFont(font);

    // 创建功能按钮
    newFuncPtb();

    // 创建日志展示窗口对象
    m_pQTextBrowser = new QTextBrowser(this);
    m_pQTextBrowser->setMinimumHeight(80);
    addWidget2BaseLayout(m_pQTextBrowser);

    // 添加顶部工具栏
    newBaseClose();

    // 所有按钮统一设置风格
    setBaseFont(gstFont);
}

void FormBase64Proc::newFuncPtb()
{
    // 创建工具栏按钮
    m_pQPBCheckImg = NewPTBWithIcon(QStringLiteral("图像识别"), QStringLiteral("根据解码结果，自动识别为图像时，实时展示图片"), QIcon(QString(":/operationIcons/resource/operationIcons/check_wait.ico")), this);
    m_pQPBUpdate = NewPTBWithIcon(QStringLiteral("结果刷新"), QStringLiteral("自动更新编码结果到剪贴板，或保存至本地文件(如已设置文件路径)"), QIcon(QString(":/operationIcons/resource/operationIcons/check_wait.ico")), this);

    connect(m_pQPBCheckImg, SIGNAL(clicked()), this, SLOT(pb_CheckImg()));
    connect(m_pQPBUpdate, SIGNAL(clicked()), this, SLOT(pb_Update()));

    addBaseToolFunc(m_pQPBCheckImg);
    addBaseToolFunc(m_pQPBUpdate);

    // 后续按钮靠右排列
    addBaseFuncStretch();

    QPushButton *pQPBClearInput = NewPTBWithIcon(QStringLiteral("清空"), QStringLiteral("清空输入内容"), QIcon(QString(":/operationIcons/resource/operationIcons/file_delete.ico")), this);
    connect(pQPBClearInput, SIGNAL(clicked()), this, SLOT(pb_ClearInput()));
    addBaseToolFunc(pQPBClearInput, true);

    // 创建中间层按钮
    QHBoxLayout *pLayoutFunc = new QHBoxLayout;
    addLayout2BaseLayout(pLayoutFunc);

    QPushButton *pQPBEncode = NewPTBWithIcon(QStringLiteral("编码"), QStringLiteral("执行Base64编码"), QIcon(QString(":/operationIcons/resource/operationIcons/word_encode.ico")), this);
    QPushButton *pQPBDecode = NewPTBWithIcon(QStringLiteral("解码"), QStringLiteral("执行Base64解码"), QIcon(QString(":/operationIcons/resource/operationIcons/word_decode.ico")), this);
    QPushButton *pQPBCopy = NewPTBWithIcon(QStringLiteral("拷贝"), QStringLiteral("将已编解码数据复制到剪贴板"), QIcon(QString(":/operationIcons/resource/operationIcons/file_copy.ico")), this);
    QPushButton *pQPBSave = NewPTBWithIcon(QStringLiteral("保存"), QStringLiteral("保存编解码数据到本地"), QIcon(QString(":/operationIcons/resource/operationIcons/file_save.ico")), this);
    m_pQPBDellog = NewPTBWithIcon(QStringLiteral("清空"), QStringLiteral("清空执行日志内容"), QIcon(QString(":/operationIcons/resource/operationIcons/message_clear.ico")), this);
    m_pQPBlog = NewPTBWithIcon(QStringLiteral("隐藏"), QStringLiteral("隐藏执行日志窗口"), QIcon(QString(":/operationIcons/resource/operationIcons/line_down.ico")), this);
    m_pQPBPrint = NewPTBWithIcon(QStringLiteral("打印"), QStringLiteral("将编解码数据打印到控制台"), QIcon(QString(":/operationIcons/resource/operationIcons/print.ico")), this);

    connect(pQPBEncode, SIGNAL(clicked()), this, SLOT(pb_Encode()));
    connect(pQPBDecode, SIGNAL(clicked()), this, SLOT(pb_Decode()));
    connect(m_pQPBPrint, SIGNAL(clicked()), this, SLOT(pb_PrintData()));
    connect(pQPBCopy, SIGNAL(clicked()), this, SLOT(pb_Copy2Clipboard()));
    connect(pQPBSave, SIGNAL(clicked()), this, SLOT(pb_Save()));
    connect(m_pQPBDellog, SIGNAL(clicked()), this, SLOT(pb_Clearlog()));
    connect(m_pQPBlog, SIGNAL(clicked()), this, SLOT(pb_LogSet()));

    // 添加到工具栏
    pLayoutFunc->addWidget(m_pQPBlog);
    pLayoutFunc->addWidget(m_pQPBDellog);
    pLayoutFunc->addWidget(m_pQPBPrint);
    pLayoutFunc->addWidget(pQPBCopy);
    pLayoutFunc->addWidget(pQPBSave);

    pLayoutFunc->addStretch();

    pLayoutFunc->addWidget(pQPBEncode);
    pLayoutFunc->addSpacing(FUNC_PUSHBUTTON_SPACE * 2);
    pLayoutFunc->addWidget(pQPBDecode);

    addFunc2Vec(m_pQPBlog);
    addFunc2Vec(m_pQPBDellog);
    addFunc2Vec(m_pQPBPrint);
    addFunc2Vec(pQPBCopy);
    addFunc2Vec(pQPBSave);
    addFunc2Vec(pQPBEncode);
    addFunc2Vec(pQPBDecode);
}

void FormBase64Proc::pb_Decode()
{
    std::string strData = m_pQTextEdit->document()->toPlainText().toStdString();
    if(strData.empty())
    {
        myMessageBoxShowOne(m_eProcType, MSG_SHOW_ERROR, QString("请输入待编码的文本和文件路径，文件支持拖拽到文本框!"));
        return;
    }

    if(false == isFileAndRead(strData))
    {
        return;
    }

    int iSrcDataLen = strData.length();
    m_strLastData.clear();
    m_strLastData = base64_decode(strData);
    int iDetLen = m_strLastData.length();
    QString strShow = QString("解码 -> 源数据长度: %1, 编码后长度值: %2, 单位:btye    ").arg(QString::number(iSrcDataLen), QString::number(iDetLen));
    appendLogMsg(m_pQTextBrowser, MSG_TYPE_INFO, strShow);

    m_eOperation = BASE64_TYPE_DECODE;

    // 开启自动刷新，将默认将结果保存，不拷贝到剪贴板(对于是二进制数据，无意义，如有需要，手动点击拷贝即可)
    if(true == m_bUpdate && !m_strLastData.empty())
    {
        saveRetData();
    }

    // 开启解码结果图像识别，针对结果是图像的，实时展示
    checkImgAndShow();
}

void FormBase64Proc::pb_Encode()
{
    std::string strData = m_pQTextEdit->document()->toPlainText().toStdString();
    if(strData.empty())
    {
        myMessageBoxShowOne(m_eProcType, MSG_SHOW_ERROR, QString("请输入待编码的文本和文件路径，文件支持拖拽到文本框!"));
        return;
    }

    if(false == isFileAndRead(strData))
    {
        return;
    }

    int iSrcDataLen = strData.length();
    m_strLastData = base64_encode(strData.c_str(), strData.length());
    int iDetLen = m_strLastData.length();
    QString strShow = QString("编码成功 -> 源数据长度: %1, 编码后长度值: %2, 单位:btye").arg(QString::number(iSrcDataLen), QString::number(iDetLen));
    appendLogMsg(m_pQTextBrowser, MSG_TYPE_INFO, strShow);

    m_eOperation = BASE64_TYPE_ENCODE;

    if(true == m_bUpdate && !m_strLastData.empty())
    {
        QApplication::clipboard()->setText(QString::fromStdString(m_strLastData));
        strShow = QString("编码成功 -> 数据已拷贝到剪贴板, 编码数据长度: %1, 单位:btye").arg(QString::number(m_strLastData.length()));
        appendLogMsg(m_pQTextBrowser, MSG_TYPE_INFO, strShow);
        saveRetData();
    }
}

void FormBase64Proc::pb_LogSet()
{
    if(false == m_pQTextBrowser->isHidden())
    {
        m_pQPBDellog->hide();
        m_pQPBPrint->hide();
        m_pQTextBrowser->hide();
        m_pQPBlog->setText(QStringLiteral("展开"));
        m_pQPBlog->setIcon(QIcon(QString(":/operationIcons/resource/operationIcons/line_up.ico")));
        m_pQPBlog->setToolTip(QStringLiteral("展示执行日志窗口"));
        m_pQPBlog->setStatusTip(QStringLiteral("展示执行日志窗口"));
    }
    else
    {
        m_pQPBDellog->show();
        m_pQPBPrint->show();
        m_pQTextBrowser->show();
        m_pQPBlog->setText(QStringLiteral("隐藏"));
        m_pQPBlog->setIcon(QIcon(QString(":/operationIcons/resource/operationIcons/line_down.ico")));
        m_pQPBlog->setToolTip(QStringLiteral("隐藏执行日志窗口"));
        m_pQPBlog->setStatusTip(QStringLiteral("隐藏执行日志窗口"));
    }
}
void FormBase64Proc::pb_Copy2Clipboard()
{
    if(false == hasData())
    {
        return;
    }

    QApplication::clipboard()->setText(QString::fromStdString(m_strLastData));
    QString strShow = QString("成功将结果复制到粘贴板，数据长度 %1 byte").arg(QString::number(m_strLastData.length()));
    if(true == m_pQTextBrowser->isHidden())
    {
        // 日志窗口为隐藏状态，采用弹框方式提醒，否则直接打印在日志窗口中
        myMessageBoxShowOne(m_eProcType, MSG_SHOW_INFO, strShow);
    }

    appendLogMsg(m_pQTextBrowser, MSG_TYPE_INFO, strShow);
}

// 保存文件
void FormBase64Proc::pb_Save()
{
    if(true == hasData())
    {
        saveRetData();
    }
}

// 自动更新结果数据到剪贴板，或保存至本地文件
void FormBase64Proc::pb_Update()
{
    m_bUpdate = !m_bUpdate;
    QString strShow;
    if(true == m_bUpdate)
    {
        // 自动保存方式
        m_pQPBUpdate->setIcon(QIcon(QString(":/operationIcons/resource/operationIcons/check_yes.ico")));
        strShow = QString("编解码结果自动保存到本地文件，编码结果自动拷贝到剪贴板");
        appendLogMsg(m_pQTextBrowser, MSG_TYPE_INFO, strShow);
    }
    else
    {
        m_pQPBUpdate->setIcon(QIcon(QString(":/operationIcons/resource/operationIcons/check_wait.ico")));
        strShow = QString("已关闭自动保存和拷贝模式，编解码结果需手动操作");
        appendLogMsg(m_pQTextBrowser, MSG_TYPE_WARN, strShow);
    }
}

void FormBase64Proc::pb_Clearlog()
{
    m_pQTextBrowser->clear();
    appendLogMsg(m_pQTextBrowser, MSG_TYPE_WARN, QString("历史日志已清空"));
}

// 解码结果图像自动识别
void FormBase64Proc::pb_CheckImg()
{
    m_bCheckImg = !m_bCheckImg;
    QString strShow;
    if(true == m_bCheckImg)
    {
        m_pQPBCheckImg->setIcon(QIcon(QString(":/operationIcons/resource/operationIcons/check_yes.ico")));
        strShow = QString("开启解码图像结果识别，对于图像的结果自动展示图像");
        appendLogMsg(m_pQTextBrowser, MSG_TYPE_INFO, strShow);
    }
    else
    {
        m_pQPBCheckImg->setIcon(QIcon(QString(":/operationIcons/resource/operationIcons/check_wait.ico")));
        strShow = QString("已关闭解码图像结果识别模式");
        appendLogMsg(m_pQTextBrowser, MSG_TYPE_WARN, strShow);
    }
}

// 清理输入
void FormBase64Proc::pb_ClearInput()
{
    m_pQTextEdit->clear();
    m_strLastData.clear();
    appendLogMsg(m_pQTextBrowser, MSG_TYPE_INFO, QString("清空已输入文本内容"));
}

// 打印数据
void FormBase64Proc::pb_PrintData()
{
    if(true == hasData())
    {
        appendLogMsg(m_pQTextBrowser, MSG_TYPE_INFO, QString("已成功编解码数据: %1").arg(QString::fromStdString(m_strLastData)));
    }
}

// 保存数据，目标文件分类型
void FormBase64Proc::saveRetData()
{
    if(m_strSavePath.isEmpty())
    {
        m_strSavePath = AnaConfig::Instance().getSavePathWithAsk(this);
        if(m_strSavePath.isEmpty())
        {
            return;
        }

        appendLogMsg(m_pQTextBrowser, MSG_TYPE_INFO, QString("指定保存路径: %1").arg(m_strSavePath));
    }

    int iContextLen = m_strLastData.length();
    QString strFileName;
    if( BASE64_TYPE_ENCODE == m_eOperation )
    {
        strFileName = m_strSavePath + QString("/Base64_Encode_") + GetCurrentTime(SHOWTIME_TYPE_STAMP) + QString(".txt");
    }
    else if( BASE64_TYPE_DECODE == m_eOperation )
    {
        IMG_TYPE imgType = getImgTypeFromBinary(m_strLastData);
        strFileName = m_strSavePath + QString("/Base64_Decode_") + GetCurrentTime(SHOWTIME_TYPE_STAMP);
        switch (imgType) {
        case IMG_TYPE_JPG:
            strFileName += QString(".jpg");
            break;
        case IMG_TYPE_JPEG:
            strFileName += QString(".jpeg");
            break;
        case IMG_TYPE_PNG:
            strFileName += QString(".png");
            break;
        case IMG_TYPE_GIF:
            strFileName += QString(".gif");
            break;
        default:
            strFileName += QString(".bin");
            break;
        }
    }
    else
    {
        return;
    }

    if(false == WriteBinary2File(strFileName.toStdString(), m_strLastData))
    {
        QString strShow = QString("%1byte数据写入文件[%2]失败").arg(QString::number(m_strLastData.length()), strFileName);
        appendLogMsg(m_pQTextBrowser, MSG_TYPE_ERROR, strShow);
        myMessageBoxShowOne(m_eProcType, MSG_SHOW_ERROR, QString("写入文件 %1失败！\n数据长度 %2 byte").arg(strFileName, QString::number(iContextLen)));
    }
    else
    {
        QString strShow = QString("%1byte数据写入文件[%2]失败").arg(QString::number(m_strLastData.length()), strFileName);
        appendLogMsg(m_pQTextBrowser, MSG_TYPE_INFO, strShow);
        myMessageBoxShowOne(m_eProcType, MSG_SHOW_INFO, QString("写入文件 %1 成功！\n数据长度 %2 byte").arg(strFileName, QString::number(iContextLen)));
    }
}

// 是否存在已编解码结果数据，未有有效结果数据，返回并提示错误
bool FormBase64Proc::hasData()
{
    bool bHasData = !m_strLastData.empty();
    if(false == bHasData)
    {
        myMessageBoxShowOne(m_eProcType, MSG_SHOW_ERROR, QString("处理结果中未找到有效待保存数据，请先输入内容后执行Base64编解码操作！"));
    }

    return bHasData;
}

// 判断是否为文件，对于文件内容则读取文件数据
bool FormBase64Proc::isFileAndRead(std::string &strSrc)
{
    bool bRet = false;

    do
    {
        if(strSrc.empty())
        {
            qDebug() << "FormBase64Proc::isFileAndRead strSrc is empty, len: " << strSrc.length();
            break;
        }

        std::string strSub = strSrc.substr(0, 8);
        if(strSub == std::string("file:///"))
        {
            // 确认是属于文件路径
            QString strFile = QString::fromStdString(strSrc.substr(8, strSrc.length() - 8));
            if(false == FileExist(strFile))
            {
                 myMessageBoxShowOne(m_eProcType, MSG_SHOW_ERROR, QString("指定文件[%1]不存在，请确认!").arg(strFile));
                 break;
            }
            else
            {
                // 指定文件存在，则需获取该文件全部内容，以二进制方式读取整个文件，并将数据通过参数返回
                strSrc.clear();
                if(false == ReadFileByBinary(strFile.toStdString(), strSrc))
                {
                    // 读取失败，发出警告
                    myMessageBoxShowOne(m_eProcType, MSG_SHOW_ERROR, QString("指定文件[%1]读取数据失败，请确认!").arg(strFile));
                    break;
                }
                else
                {
                    QString strShow = QString("文件[%1]读取成功，数据长度: %2 btye").arg(strFile, QString::number(strSrc.length()));
                    appendLogMsg(m_pQTextBrowser, MSG_TYPE_INFO, strShow);
                }
            }
        }

        bRet = true;
    }while(0);

    return bRet;
}

// 检查解码结果是否为图像，并将图像结果展示出来
void FormBase64Proc::checkImgAndShow()
{
    if(false == m_bCheckImg)
    {
        return;
    }

    IMG_TYPE imgType = getImgTypeFromBinary(m_strLastData);

    qDebug() << "checkImgAndShow" << imgType;
    if(IMG_TYPE_UNKNOW == imgType)
    {
        return;
    }

}

// 设置风格
void FormBase64Proc::setFont(const QFont &stFont)
{
    setBaseFont(stFont);
}
